<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
    

<head><meta name="generator" content="Hexo 3.8.0">
    <meta charset="utf-8">
    <meta name="keywords" content="WebRTC05入门教程02, 炫杉">
    <meta name="description" content="p2p的核心RTCPeerConnection APIs的介绍

RTCPeerConnection APIs简单样例：var pc = RTCPeerConnection(config);
config包含 key, iceServers">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <title>WebRTC05入门教程02 | 炫杉</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/css/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery-2.2.0.min.js"></script>
<link rel="stylesheet" href="/css/prism-tomorrow.css" type="text/css">
<link rel="stylesheet" href="/css/prism-line-numbers.css" type="text/css"></head>

</head>

<body>

<header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="container">
            <div class="nav-wrapper">
                <div class="brand-logo">
                    <a href="/" class="waves-effect waves-light">
                        
                        <img src="/medias/logo.png" class="logo-img hide-on-small-only">
                        
                        <span class="logo-span">炫杉</span>
                    </a>
                </div>
                <a href="#" data-activates="mobile-nav" class="button-collapse"><i class="fa fa-navicon"></i></a>
<ul class="right">
    
    <li class="hide-on-med-and-down">
        <a href="/" class="waves-effect waves-light">
            
            <i class="fa fa-home"></i>
            
            <span>Index</span>
        </a>
    </li>
    
    <li class="hide-on-med-and-down">
        <a href="/tags" class="waves-effect waves-light">
            
            <i class="fa fa-tags"></i>
            
            <span>Tags</span>
        </a>
    </li>
    
    <li class="hide-on-med-and-down">
        <a href="/categories" class="waves-effect waves-light">
            
            <i class="fa fa-bookmark"></i>
            
            <span>Categories</span>
        </a>
    </li>
    
    <li class="hide-on-med-and-down">
        <a href="/archives" class="waves-effect waves-light">
            
            <i class="fa fa-archive"></i>
            
            <span>Archives</span>
        </a>
    </li>
    
    <li class="hide-on-med-and-down">
        <a href="/about" class="waves-effect waves-light">
            
            <i class="fa fa-user-circle-o"></i>
            
            <span>About</span>
        </a>
    </li>
    
    <li class="hide-on-med-and-down">
        <a href="https://oo1.win" class="waves-effect waves-light">
            
            <i class="fa fa-star"></i>
            
            <span>Bookmark</span>
        </a>
    </li>
    
    <li>
        <a id="toggleSearch" class="waves-effect waves-light">
            <i id="searchIcon" class="mdi-action-search"></i>
        </a>
    </li>

</ul>

<div class="side-nav" id="mobile-nav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">炫杉</div>
        <div class="logo-desc">
            
            提着鸟笼的老头站在一旁拍我肩膀 我猜他想的应该和我一样
            
        </div>
    </div>

    <ul class="menu-list mobile-menu-list">
        
        <li>
            <a href="/" class="waves-effect waves-light">
                
                <i class="fa fa-fw fa-home"></i>
                
                Index
            </a>
        </li>
        
        <li>
            <a href="/tags" class="waves-effect waves-light">
                
                <i class="fa fa-fw fa-tags"></i>
                
                Tags
            </a>
        </li>
        
        <li>
            <a href="/categories" class="waves-effect waves-light">
                
                <i class="fa fa-fw fa-bookmark"></i>
                
                Categories
            </a>
        </li>
        
        <li>
            <a href="/archives" class="waves-effect waves-light">
                
                <i class="fa fa-fw fa-archive"></i>
                
                Archives
            </a>
        </li>
        
        <li>
            <a href="/about" class="waves-effect waves-light">
                
                <i class="fa fa-fw fa-user-circle-o"></i>
                
                About
            </a>
        </li>
        
        <li>
            <a href="https://oo1.win" class="waves-effect waves-light">
                
                <i class="fa fa-fw fa-star"></i>
                
                Bookmark
            </a>
        </li>
        
        
    </ul>

    <div class="social-link"><a href="https://github.com/ixsim" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
    <i class="fa fa-github"></i>
</a>

<a href="mailto:chenjiayin1990@163.com" class="tooltipped" target="_blank" data-tooltip="邮件:chinalixs@qq.com" data-position="top" data-delay="50">
    <i class="fa fa-envelope-open"></i>
</a>

<a href="http://wpa.qq.com/msgrd?v=3&uin=6956820&site=qq&menu=yes" class="tooltipped" data-tooltip="QQ: 6956820" data-position="top" data-delay="50">
    <i class="fa fa-qq"></i>
</a>
</div>
</div>

            </div>
        </div>

        
    </nav>
</header>



<div class="bg-cover post-cover" style="background-image: url('/medias/featureimages/1.jpg')">
    <div class="container">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <div class="description center-align post-title">
                        WebRTC05入门教程02
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>



<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1,
    #articleContent h2,
    #articleContent h3,
    #articleContent h4,
    #articleContent h5,
    #articleContent h6 {
        padding-top: 76px;
        margin-top: -76px;
    }

    #articleContent h1 {
        line-height: 3.5rem;
    }

    #articleContent h2 {
        line-height: 3.2rem;
    }

    #articleContent h3 {
        line-height: 2.8rem;
    }

    #articleContent h4 {
        line-height: 2.5rem;
    }

    #articleContent h5 {
        line-height: 2.2rem;
    }

    #articleContent h6 {
        line-height: 1.9rem;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        margin: 35px 0 15px 0;
        padding-left: 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #toc-content .is-active-link::before {
        background-color: #42b983;
    }
</style>
<div class="row">
    <div class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            
            <div class="article-tag">
                
                <a href="/tags/Demo/" target="_blank">
                    <span class="chip bg-color">Demo</span>
                </a>
                
                <a href="/tags/tutorial/" target="_blank">
                    <span class="chip bg-color">tutorial</span>
                </a>
                
            </div>
            
            <div class="post-info">
                
                <span class="post-cate">
                    <i class="fa fa-bookmark fa-fw icon-category"></i>
                    
                    <a href="/categories/WebRTC/" class="post-category" target="_blank">
                        WebRTC
                    </a>
                    
                </span>
                

                <span class="post-date">
                    <i class="fa fa-clock-o fa-fw"></i>2018-08-30
                </span>
            </div>
        </div>
        <hr>
        <div class="card-content article-card-content">
            <div id="articleContent">
                <p>p2p的核心<strong>RTCPeerConnection APIs</strong>的介绍</p>
<a id="more"></a>
<h1 id="RTCPeerConnection-APIs"><a href="#RTCPeerConnection-APIs" class="headerlink" title="RTCPeerConnection APIs"></a>RTCPeerConnection APIs</h1><p>简单样例：<code>var pc = RTCPeerConnection(config);</code></p>
<p>config包含 key, iceServers。这是一个有着STUN和TURN服务器信息的URL对象数组，用于查找ICE候选。你可以在<a href="https://code.google.com找到一些可用的公共STUN服务器" target="_blank" rel="noopener">https://code.google.com找到一些可用的公共STUN服务器</a>.</p>
<p>这是user’s flow用户流的一个例子：</p>
<ul>
<li>注册<em>onincecandidate</em>句柄。收到时发送ICE candidates给其他peer。</li>
<li>注册<em>onaddstream</em>句柄。一旦收到远端peer接收，则控制视频流的展示</li>
<li>注册<em>message</em>句柄。你的信令服务器应该也有一个句柄用于收消息。如果message包含<em>RTCSeesionDescription</em>对象，则使用<em>setRemoteDescription()</em>方法将其加入到<u><em>RTCPeerConnection</em></u><u>对象</u>。如果message包含<em>RTCIceCandidate</em>对象，使用<em>addIceCandidate()</em>方法将其加入到<u><em>RTCPeerConnection</em></u><u>对象</u>。</li>
<li>用<em>getUserMedia()</em>设置本地媒体流，用 <em>addStream()</em> 方法将其加入到<u><em>RTCPeerConnection</em></u><u>对象</u>。</li>
<li>开始 <strong>offer/answer</strong> 协商。这是唯一一个[主叫流caller flow]和[被叫callee]有所不同的一步。caller用<strong><u><em>createOffer()</em></u></strong>方法开始协商，注册一个接收 <em><u>RTCSessionDescription</u></em>对象的callback。这个callback应使用<em>setLocalDescriptionI()</em>方法添加这个<em><u>RTCSessionDescription</u></em>到你的<em>RTCPeerConnection</em>。  最后，caller应该用信令服务器发送这个<em><u>RTCPeerConnection</u></em>到远端peer。 callee注册相同的callback，但是用的是<strong><u><em>createAnswer()</em></u></strong>方法。仅在收到caller的<strong>offer</strong>后callee flow初始化完成。</li>
</ul>
<h2 id="RTCPeerConnection-API"><a href="#RTCPeerConnection-API" class="headerlink" title="RTCPeerConnection API"></a>RTCPeerConnection API</h2><h3 id="特性"><a href="#特性" class="headerlink" title="特性"></a>特性</h3><ul>
<li><p><strong>RTCPeerConnection.iceConnectionState (read only)</strong> − 返回一个RTCIceConnectionState</p>
<p>enum描述连接状态.  值改变时触发一个iceconnectionstatechange 事件。</p>
<p>可能的值：</p>
<ul>
<li><strong>new</strong> − ICE代理在等待远程候选或收集地址。</li>
<li><strong>checking</strong> − ICE 代理 有远程候选但还没有发现连接。</li>
<li><strong>connected</strong> − ICE 代理已经发现了可用连接，但仍然在checking更远的候选以便更好地连接。</li>
<li><strong>completed</strong> − ICE 代理已经发现了可用连接并停止检测远程候选。</li>
<li><strong>failed</strong> − ICE 代理已经检查完所有的远程候选们但找不到一个可匹配的。</li>
<li><strong>disconnected</strong> − 至少有一个部分不再工作。</li>
<li><strong>closed</strong> − ICE 代理关闭。</li>
</ul>
</li>
<li><p><strong>RTCPeerConnection.iceGatheringState (read only)</strong> − 返回一个 RTCIceGatheringState enum描述连接的ICE收集状态−</p>
<ul>
<li><strong>new</strong> − 对象刚被创建</li>
<li><strong>gathering</strong> − ICE 代理在收集候选过程中</li>
<li><strong>complete</strong> ICE 代理 已经完成收集</li>
</ul>
</li>
<li><p><strong>RTCPeerConnection.localDescription (read only)</strong> − 返回一个RTCSessionDescription 描述本地节点。如果还没有被设置则为null</p>
</li>
<li><p><strong>RTCPeerConnection.peerIdentity (read only)</strong> − 返回一个 RTCIdentityAssertion. 包含一个idp(domain name) 和一个描述远程peer的name</p>
</li>
<li><p><strong>RTCPeerConnection.remoteDescription (read only)</strong> − 返回一个 RTCSessionDescription 描述远程节点. 没设置为null</p>
</li>
<li><p><strong>RTCPeerConnection.signalingState (read only)</strong> − 返回一个 RTCSignalingState enum描述本地连接的信令状态。这个状态描述SDP offer。值改变时触发一个signalingstatechange 事件。可能的值： </p>
<ul>
<li><strong>stable</strong> − 初始状态。不存在 SDP offer/answer 交换。</li>
<li><strong>have-local-offer</strong> − 连接的本地端已经在当地申请了一个SDP offer.</li>
<li><strong>have-remote-offer</strong> − 连接的远程端已经在当地申请了一个SDP offer.</li>
<li><strong>have-local-pranswer</strong> − 一个远程SDP offer已申请，本地已申请一个SDP pranswer.</li>
<li><strong>have-remote-pranswer</strong> − 一个本地SDP已申请，远程已申请一个SDP pranswer.</li>
<li><strong>closed</strong> − 连接关闭。</li>
</ul>
</li>
</ul>
<h3 id="事件句柄"><a href="#事件句柄" class="headerlink" title="事件句柄"></a>事件句柄</h3><p>下面是常用的几个</p>
<table>
<thead>
<tr>
<th style="text-align:right">Event Handlers</th>
<th style="text-align:left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onaddstream</strong></td>
<td style="text-align:left">addstream 事件触发. 当MediaSteam被远程peer添加到连接中时发送这个事件。</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.ondatachannel</strong></td>
<td style="text-align:left">datachannel 事件触发. 当RTCDataChannel添加到连接中时发送这个事件。</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onicecandidate</strong></td>
<td style="text-align:left">Icecandidate 事件触发. 当RTCIceCandidate对象添加到script中时发送这个事件。</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.oniceconnectionstatechange</strong></td>
<td style="text-align:left">This handler is called when Iceconnectionstatechange 事件触发时. This event is sent 当value of iceConnectionState changes.</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onidentityresult</strong></td>
<td style="text-align:left">This handler is called when Identityresult 事件触发时. This event is sent when an identity assertion is generated during the creating of an offer or an answer of via getIdentityAssertion().</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onidpassertionerror</strong></td>
<td style="text-align:left">This handler is called when Idpassertionerror 事件触发时. This event is sent when IdP (Identitry Provider) finds an error while generating an identity assertion.</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onidpvalidation</strong></td>
<td style="text-align:left">This handler is called when Idpvalidationerror 事件触发时. This event is sent when IdP (Identitry Provider) finds an error while validating an identity assertion.</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onnegotiationneeded</strong></td>
<td style="text-align:left">This handler is called 当negotiationneeded 事件触发时. This event is sent by the browser to inform the negotiation will be required at some point in the future.</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onpeeridentity</strong></td>
<td style="text-align:left">This handler is called 当peeridentity 事件触发时. This event is sent when a peer identity has been set and verified on this connection.</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onremovestream</strong></td>
<td style="text-align:left">This handler is called 当signalingstatechange 事件触发时. This event is sent 当value of signalingState changes.</td>
</tr>
<tr>
<td style="text-align:right"><strong>RTCPeerConnection.onsignalingstatechange</strong></td>
<td style="text-align:left">This handler is called 当removestream 事件触发时. This event is sent when a MediaStream is removed from this connection.</td>
</tr>
</tbody>
</table>
<h3 id="Methods"><a href="#Methods" class="headerlink" title="Methods"></a>Methods</h3><p>下面是常用的几个</p>
<table>
<thead>
<tr>
<th>S.No.</th>
<th>Methods &amp; Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td><strong>RTCPeerConnection()</strong>返回一个 new RTCPeerConnection object.</td>
</tr>
<tr>
<td>2</td>
<td><strong>RTCPeerConnection.createOffer()</strong>Creates an offer(request) to find a remote peer. The two first parameters of this method are success and error callbacks. The optional third parameter are options, like enabling audio or video streams.</td>
</tr>
<tr>
<td>3</td>
<td><strong>RTCPeerConnection.createAnswer()</strong>Creates an answer to the offer received by the remote peer during the offer/answer negotiation process. The two first parameters of this method are success and error callbacks. The optional third parameter are options for the answer to be created.</td>
</tr>
<tr>
<td>4</td>
<td><strong>RTCPeerConnection.setLocalDescription()</strong>Changes the local connection description. The description defines the properties of the connection. The connection must be able to support both old and new descriptions. The method takes three parameters, RTCSessionDescription object, callback if the change of description succeeds, callback if the change of description fails.</td>
</tr>
<tr>
<td>5</td>
<td><strong>RTCPeerConnection.setRemoteDescription()</strong>Changes the remote connection description. The description defines the properties of the connection. The connection must be able to support both old and new descriptions. The method takes three parameters, RTCSessionDescription object, callback if the change of description succeeds, callback if the change of description fails.</td>
</tr>
<tr>
<td>6</td>
<td><strong>RTCPeerConnection.updateIce()</strong>Updates ICE 代理 process of pinging remote candidates and gathering local candidates.</td>
</tr>
<tr>
<td>7</td>
<td><strong>RTCPeerConnection.addIceCandidate()</strong>Provides a remote candidate to ICE 代理.</td>
</tr>
<tr>
<td>8</td>
<td><strong>RTCPeerConnection.getConfiguration()</strong>返回一个 RTCConfiguration object. It represents the configuration of the RTCPeerConnection object.</td>
</tr>
<tr>
<td>9</td>
<td><strong>RTCPeerConnection.getLocalStreams()</strong>返回一个n array of local MediaStream connection.</td>
</tr>
<tr>
<td>10</td>
<td><strong>RTCPeerConnection.getRemoteStreams()</strong>返回一个n array of remote MediaStream connection.</td>
</tr>
<tr>
<td>11</td>
<td><strong>RTCPeerConnection.getStreamById()</strong>Returns local or remote MediaStream by the given ID.</td>
</tr>
<tr>
<td>12</td>
<td><strong>RTCPeerConnection.addStream()</strong>Adds a MediaStream as a local source of video or audio.</td>
</tr>
<tr>
<td>13</td>
<td><strong>RTCPeerConnection.removeStream()</strong>Removes a MediaStream as a local source of video or audio.</td>
</tr>
<tr>
<td>14</td>
<td><strong>RTCPeerConnection.close()</strong>Closes a connection.</td>
</tr>
<tr>
<td>15</td>
<td><strong>RTCPeerConnection.createDataChannel()</strong>Creates a new RTCDataChannel.</td>
</tr>
<tr>
<td>16</td>
<td><strong>RTCPeerConnection.createDTMFSender()</strong>Creates a new RTCDTMFSender, associated to a specific MediaStreamTrack. Allows to send DTMF (Dual-tone multifrequency) phone signaling over the connection.</td>
</tr>
<tr>
<td>17</td>
<td><strong>RTCPeerConnection.getStats()</strong>Creates a new RTCStatsReport that contains statistics concerning the connection.</td>
</tr>
<tr>
<td>18</td>
<td><strong>RTCPeerConnection.setIdentityProvider()</strong>Sets IdP. Takes three parameters − the name, the protocol used to communicate and an optional username.</td>
</tr>
<tr>
<td>19</td>
<td><strong>RTCPeerConnection.getIdentityAssertion()</strong>Gathers an identity assertion. It is not expected to deal with this method in the application. So you may call it explicitly only to anticipate the need.</td>
</tr>
</tbody>
</table>
<h3 id="建立一个连接"><a href="#建立一个连接" class="headerlink" title="建立一个连接"></a>建立一个连接</h3><p>先把信令服务器跑起来。</p>
<p>代码：<code>server.js</code></p>
<pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token comment" spellcheck="true">//信令服务器代码请转到入门教程03</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span></span></code></pre>
<p>然后<code>node server</code> 把上面的信令服务器跑起来。</p>
<p>（教程01说过要安装好nodejs环境）</p>
<p>如果想测试是否跑起来了，可以用wscat命令：</p>
<p><code>wscat -c ws://localhost:9090</code> </p>
<p>然后是前端：<code>index.html</code></p>
<pre class="line-numbers language-html"><code class="language-html">&lt;html lang = "en"> 
   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>head</span><span class="token punctuation">></span></span> 
      &lt;meta charset = "utf-8" /> 
   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>head</span><span class="token punctuation">></span></span>

   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>body</span><span class="token punctuation">></span></span> 

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span> 
         &lt;input type = "text" id = "loginInput" /> 
         &lt;button id = "loginBtn">Login<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span> 
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> 

      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;</span>div</span><span class="token punctuation">></span></span> 
         &lt;input type = "text" id = "otherUsernameInput" />
         &lt;button id = "connectToOtherUsernameBtn">Establish connection<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>button</span><span class="token punctuation">></span></span> 
      <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>div</span><span class="token punctuation">></span></span> 

      &lt;script src = "client.js"><span class="token script language-javascript"></span><span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>script</span><span class="token punctuation">></span></span>

   <span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>body</span><span class="token punctuation">></span></span>

<span class="token tag"><span class="token tag"><span class="token punctuation">&lt;/</span>html</span><span class="token punctuation">></span></span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>这个不用解释吧。就是两个输入框两个按钮。</p>
<p>最后是客户端js文件：<code>client.js</code></p>
<pre class="line-numbers language-javascript"><code class="language-javascript"><span class="token keyword">var</span> connection <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">WebSocket</span><span class="token punctuation">(</span><span class="token string">'ws://localhost:9090'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">var</span> name <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span> 

<span class="token keyword">var</span> loginInput <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#loginInput'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">var</span> loginBtn <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#loginBtn'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">var</span> otherUsernameInput <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#otherUsernameInput'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">var</span> connectToOtherUsernameBtn <span class="token operator">=</span> document<span class="token punctuation">.</span><span class="token function">querySelector</span><span class="token punctuation">(</span><span class="token string">'#connectToOtherUsernameBtn'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token keyword">var</span> connectedUser<span class="token punctuation">,</span> myConnection<span class="token punctuation">;</span>

<span class="token comment" spellcheck="true">//当一个用户点击登录 </span>
loginBtn<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span>event<span class="token punctuation">)</span><span class="token punctuation">{</span> 
   name <span class="token operator">=</span> loginInput<span class="token punctuation">.</span>value<span class="token punctuation">;</span> 

   <span class="token keyword">if</span><span class="token punctuation">(</span>name<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//发送一个message{ </span>
         type<span class="token punctuation">:</span> <span class="token string">"login"</span><span class="token punctuation">,</span> 
         name<span class="token punctuation">:</span> name 
      <span class="token punctuation">}</span>
      <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span> 
         type<span class="token punctuation">:</span> <span class="token string">"login"</span><span class="token punctuation">,</span> 
         name<span class="token punctuation">:</span> name 
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
   <span class="token punctuation">}</span> 

<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

<span class="token comment" spellcheck="true">//handle messages from the server </span>
connection<span class="token punctuation">.</span>onmessage <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>message<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
   console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Got message"</span><span class="token punctuation">,</span> message<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span>
   <span class="token keyword">var</span> data <span class="token operator">=</span> JSON<span class="token punctuation">.</span><span class="token function">parse</span><span class="token punctuation">(</span>message<span class="token punctuation">.</span>data<span class="token punctuation">)</span><span class="token punctuation">;</span> 

   <span class="token keyword">switch</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>type<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
      <span class="token keyword">case</span> <span class="token string">"login"</span><span class="token punctuation">:</span> 
         <span class="token function">onLogin</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>success<span class="token punctuation">)</span><span class="token punctuation">;</span> 
         <span class="token keyword">break</span><span class="token punctuation">;</span> 
      <span class="token keyword">case</span> <span class="token string">"offer"</span><span class="token punctuation">:</span> 
         <span class="token function">onOffer</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>offer<span class="token punctuation">,</span> data<span class="token punctuation">.</span>name<span class="token punctuation">)</span><span class="token punctuation">;</span> 
         <span class="token keyword">break</span><span class="token punctuation">;</span> 
      <span class="token keyword">case</span> <span class="token string">"answer"</span><span class="token punctuation">:</span> 
         <span class="token function">onAnswer</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>answer<span class="token punctuation">)</span><span class="token punctuation">;</span> 
         <span class="token keyword">break</span><span class="token punctuation">;</span> 
      <span class="token keyword">case</span> <span class="token string">"candidate"</span><span class="token punctuation">:</span> 
         <span class="token function">onCandidate</span><span class="token punctuation">(</span>data<span class="token punctuation">.</span>candidate<span class="token punctuation">)</span><span class="token punctuation">;</span> 
         <span class="token keyword">break</span><span class="token punctuation">;</span> 
      <span class="token keyword">default</span><span class="token punctuation">:</span> 
         <span class="token keyword">break</span><span class="token punctuation">;</span> 
   <span class="token punctuation">}</span> 
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment" spellcheck="true">//当用户登录</span>
<span class="token keyword">function</span> <span class="token function">onLogin</span><span class="token punctuation">(</span>success<span class="token punctuation">)</span> <span class="token punctuation">{</span> 

   <span class="token keyword">if</span> <span class="token punctuation">(</span>success <span class="token operator">===</span> <span class="token boolean">false</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment" spellcheck="true">//重名了</span>
      <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"oops...try a different username"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
   <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> 
      <span class="token comment" spellcheck="true">//创建RTCPeerConnection对象 </span>

      <span class="token keyword">var</span> configuration <span class="token operator">=</span> <span class="token punctuation">{</span> 
         <span class="token string">"iceServers"</span><span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">{</span> <span class="token string">"url"</span><span class="token punctuation">:</span> <span class="token string">"stun:stun.1.google.com:19302"</span> <span class="token punctuation">}</span><span class="token punctuation">]</span> 
      <span class="token punctuation">}</span><span class="token punctuation">;</span> 

      myConnection <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">webkitRTCPeerConnection</span><span class="token punctuation">(</span>configuration<span class="token punctuation">)</span><span class="token punctuation">;</span> 
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"RTCPeerConnection object was created"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
      console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>myConnection<span class="token punctuation">)</span><span class="token punctuation">;</span> 

      <span class="token comment" spellcheck="true">//setup ice handling</span>
      <span class="token comment" spellcheck="true">//当浏览器发现一个ICE候选我们就发送给其他人。 </span>
      myConnection<span class="token punctuation">.</span>onicecandidate <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>event<span class="token punctuation">)</span> <span class="token punctuation">{</span> 

         <span class="token keyword">if</span> <span class="token punctuation">(</span>event<span class="token punctuation">.</span>candidate<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
            <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span> 
               type<span class="token punctuation">:</span> <span class="token string">"candidate"</span><span class="token punctuation">,</span> 
               candidate<span class="token punctuation">:</span> event<span class="token punctuation">.</span>candidate 
            <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
         <span class="token punctuation">}</span> 
      <span class="token punctuation">}</span><span class="token punctuation">;</span> 
   <span class="token punctuation">}</span> 
<span class="token punctuation">}</span><span class="token punctuation">;</span>

connection<span class="token punctuation">.</span>onopen <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 
   console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Connected"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span><span class="token punctuation">;</span>

connection<span class="token punctuation">.</span>onerror <span class="token operator">=</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
   console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token string">"Got error"</span><span class="token punctuation">,</span> err<span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment" spellcheck="true">// 发送JSON格式message的Alias(别名) </span>
<span class="token keyword">function</span> <span class="token function">send</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span> <span class="token punctuation">{</span> 

   <span class="token keyword">if</span> <span class="token punctuation">(</span>connectedUser<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
      message<span class="token punctuation">.</span>name <span class="token operator">=</span> connectedUser<span class="token punctuation">;</span> 
   <span class="token punctuation">}</span> 

   connection<span class="token punctuation">.</span><span class="token function">send</span><span class="token punctuation">(</span>JSON<span class="token punctuation">.</span><span class="token function">stringify</span><span class="token punctuation">(</span>message<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span><span class="token punctuation">;</span>

<span class="token comment" spellcheck="true">//setup a peer connection with another user </span>
connectToOtherUsernameBtn<span class="token punctuation">.</span><span class="token function">addEventListener</span><span class="token punctuation">(</span><span class="token string">"click"</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 

   <span class="token keyword">var</span> otherUsername <span class="token operator">=</span> otherUsernameInput<span class="token punctuation">.</span>value<span class="token punctuation">;</span> 
   connectedUser <span class="token operator">=</span> otherUsername<span class="token punctuation">;</span>

   <span class="token keyword">if</span> <span class="token punctuation">(</span>otherUsername<span class="token punctuation">.</span>length <span class="token operator">></span> <span class="token number">0</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> 
      <span class="token comment" spellcheck="true">//make an offer </span>
      myConnection<span class="token punctuation">.</span><span class="token function">createOffer</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span>offer<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
         console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
         <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span> 
            type<span class="token punctuation">:</span> <span class="token string">"offer"</span><span class="token punctuation">,</span> 
            offer<span class="token punctuation">:</span> offer 
         <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>

         myConnection<span class="token punctuation">.</span><span class="token function">setLocalDescription</span><span class="token punctuation">(</span>offer<span class="token punctuation">)</span><span class="token punctuation">;</span> 
      <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
         <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"An error has occurred."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
   <span class="token punctuation">}</span> 
<span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 

<span class="token comment" spellcheck="true">//当有人想打给我们 </span>
<span class="token keyword">function</span> <span class="token function">onOffer</span><span class="token punctuation">(</span>offer<span class="token punctuation">,</span> name<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
   connectedUser <span class="token operator">=</span> name<span class="token punctuation">;</span> 
   myConnection<span class="token punctuation">.</span><span class="token function">setRemoteDescription</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RTCSessionDescription</span><span class="token punctuation">(</span>offer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 

   myConnection<span class="token punctuation">.</span><span class="token function">createAnswer</span><span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span>answer<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
      myConnection<span class="token punctuation">.</span><span class="token function">setLocalDescription</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span><span class="token punctuation">;</span> 

      <span class="token function">send</span><span class="token punctuation">(</span><span class="token punctuation">{</span> 
         type<span class="token punctuation">:</span> <span class="token string">"answer"</span><span class="token punctuation">,</span> 
         answer<span class="token punctuation">:</span> answer 
      <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 

   <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span>error<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
      <span class="token function">alert</span><span class="token punctuation">(</span><span class="token string">"oops...error"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
   <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span>

<span class="token comment" spellcheck="true">//另一个用户要answer我们的offer</span>
<span class="token keyword">function</span> <span class="token function">onAnswer</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
   myConnection<span class="token punctuation">.</span><span class="token function">setRemoteDescription</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RTCSessionDescription</span><span class="token punctuation">(</span>answer<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span> 

<span class="token comment" spellcheck="true">//当我们收到ICE候选</span>
<span class="token keyword">function</span> <span class="token function">onCandidate</span><span class="token punctuation">(</span>candidate<span class="token punctuation">)</span> <span class="token punctuation">{</span> 
   myConnection<span class="token punctuation">.</span><span class="token function">addIceCandidate</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">RTCIceCandidate</span><span class="token punctuation">(</span>candidate<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> 
<span class="token punctuation">}</span>
<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre>
<p>这样 <strong>offer/answer</strong>的过程就模拟完了。可以通过输入输出在控制台看到相关信息.</p>
<h2 id="小知识点"><a href="#小知识点" class="headerlink" title="小知识点"></a>小知识点</h2><p><code>alisa</code>在 linux 中，alias 命令（注意全为小写）的功能是设置命令的别名，以简写命令，提高操作效率。根据参数的不同，该命令可查看已设定的别名，或为命令设置新的别名。对于用户自定义别名，仅当前登录期内有效；也可修改配置文件使其长期有效。</p>
<p>相关命令：<code>unalias</code></p>
<p>参考链接：<a href="https://baike.baidu.com/item/Alias/3105303#viewPageContent" target="_blank" rel="noopener">https://baike.baidu.com/item/Alias/3105303#viewPageContent</a></p>

            </div>
            <hr>

            

            <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">

<div id="article-share">
    
    <div class="social-share" data-disabled="qzone" data-wechat-qrcode-helper="<p>微信里点“发现”->“扫一扫”二维码便可查看分享。</p>"></div>
    
</div>

<script src="/libs/share/js/social-share.min.js"></script>

            <div class="reprint">
                <p>
                    <span class="reprint-tip">转载请注明: </span>
                    <a href="https://xuanshan.gitee.io" class="b-link-green">炫杉</a>
                    <i class="fa fa-angle-right fa-lg fa-fw text-color"></i>
                    <a href="/WebRTC/WebRTC05入门教程02/" class="b-link-green">WebRTC05入门教程02</a>
                </p>
            </div>
        </div>
    </div>

    

    

    

    
    <div class="livere-card card" data-aos="fade-up">
    <!-- 来必力City版安装代码 -->
    <div id="lv-container" class="card-content" data-id="city" data-uid="MTAyMC8yOTg2My82NDI5">
        <script type="text/javascript">
            (function (d, s) {
                let j, e = d.getElementsByTagName(s)[0];
                if (typeof LivereTower === 'function') {
                    return;
                }

                j = d.createElement(s);
                j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
                j.async = true;

                e.parentNode.insertBefore(j, e);
            })(document, 'script');
        </script>
        <noscript>为正常使用来必力评论功能请激活JavaScript。</noscript>
    </div>
    <!-- City版安装代码已完成 -->
</div>
    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">上一篇</div>
            <div class="card">
                <a href="/WebRTC/WebRTC06入门教程03/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/3.jpg" class="responsive-img" alt="WebRTC06入门教程03">
                        
                        <span class="card-title">WebRTC06入门教程03</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary">信令服务器分析

Signaling信令和协商Signaling and Negotiation用户建立连接的过程以信令和协商为主。有下面几个步骤：

为peer连接创建一个潜在候选项的list
用户A或应用选择一个要连接的用户B
信令层通</div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="fa fa-clock-o fa-fw icon-date"></i>2018-08-31
                        </span>
                        <span class="publish-author">
                            
                            <i class="fa fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/WebRTC/" class="post-category" target="_blank">
                                    WebRTC
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/Demo/" target="_blank">
                        <span class="chip bg-color">Demo</span>
                    </a>
                    
                    <a href="/tags/tutorial/" target="_blank">
                        <span class="chip bg-color">tutorial</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">下一篇</div>
            <div class="card">
                <a href="/WebRTC/WebRTC04Kurento流程分析/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/18.jpg" class="responsive-img" alt="WebRTC04KurentoDemo流程分析">
                        
                        <span class="card-title">WebRTC04KurentoDemo流程分析</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary">
今天的学习可以说是以失败告终了吧。也就知道了用js动态调试。大体看懂了participant.js和conference.js。但最大头的是kurento.js


关于频繁出现的 ICE candidate：ICE stands for</div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="fa fa-clock-o fa-fw icon-date"></i>2018-08-28
                            </span>
                        <span class="publish-author">
                            
                            <i class="fa fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/WebRTC/" class="post-category" target="_blank">
                                    WebRTC
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/js/" target="_blank">
                        <span class="chip bg-color">js</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>
</div>
    </div>
    <div class="col l3 hide-on-med-and-down">
        <div class="toc-widget">
            <div class="toc-title">目录</div>
            <div id="toc-content">

            </div>
        </div>
    </div>
</div>

<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingSelector: 'h1,h2, h3, h4'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h1,h2, h3, h4').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });
    });
</script>
    

</main>

<footer class="page-footer bg-color">
    <div class="container row center-align">
        <div class="col s12 m8 l8 copy-right">
            &copy;<a href="https://o--o.win" target="_blank">炫杉2018</a> 
            <a href="https://hexo.io/" target="_blank">Hexo</a> 强力驱动
              主题 <a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">matery</a>        </div>
        <div class="col s12 m4 l4 social-link"><a href="https://github.com/ixsim" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
    <i class="fa fa-github"></i>
</a>

<a href="mailto:chenjiayin1990@163.com" class="tooltipped" target="_blank" data-tooltip="邮件:chinalixs@qq.com" data-position="top" data-delay="50">
    <i class="fa fa-envelope-open"></i>
</a>

<a href="http://wpa.qq.com/msgrd?v=3&uin=6956820&site=qq&menu=yes" class="tooltipped" data-tooltip="QQ: 6956820" data-position="top" data-delay="50">
    <i class="fa fa-qq"></i>
</a>
</div>
    </div>
</footer>

<div class="progress-bar"></div>


<!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title">搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字" class="search-input" autofocus>
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script src="/js/search.js"></script>
<script type="text/javascript">
    searchFunc("/" + "search.xml", 'searchInput', 'searchResult');
</script>
<!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fa fa-angle-up"></i>
    </a>
</div>


<script src="/libs/materialize/js/materialize.min.js"></script>
<script src="/libs/masonry/masonry.pkgd.min.js"></script>
<script src="/libs/aos/aos.js"></script>
<script src="/libs/scrollprogress/scrollProgress.min.js"></script>
<script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
<script src="/js/matery.js"></script>
</body>
</html>